#pragma once

#include <mutex>
#include <condition_variable>
#include <thread>

#include "Connect.hpp"

class LoopThread
{
private:
    std::mutex _mutex;
    std::condition_variable _cond;
    EventLoop *_loop;    // 线程内实例化的对象
    std::thread _thread; // eventloop对应的线程

private:
    // 线程入口/任务（初始化线程内使用的eventloop等变量）
    void ThreadEntry()
    {
        // eventloop创建时,内部id成员变量会获取当前线程ID赋值,若在构造函数中则是主线程ID
        EventLoop loop;
        {
            // 防止获取loop前，eventloop未创建
            std::unique_lock<std::mutex> lock(_mutex);
            _loop = &loop;
            _cond.notify_all();
        }

        loop.Start();
    }

public:
    LoopThread() : _loop(nullptr), _thread(std::thread(&LoopThread::ThreadEntry, this)) {}

    /*返回当前线程关联的EventLoop对象指针*/
    EventLoop *GetLoop()
    {
        EventLoop *loop = nullptr;
        {
            std::unique_lock<std::mutex> lock(_mutex); // 加锁
            _cond.wait(lock, [&]()
                       { return _loop != nullptr; }); // loop为NULL就一直阻塞
            loop = _loop;
        }
        return loop;
    }
};

class LoopThreadPool
{
private:
    int _threadCount;                     // 线程池内线程数量
    int _threadIndex;                     // 当前线程池中所指的线程（用于遍历线程池）
    EventLoop *_baseThreadLoop;           // 主线程loop
    std::vector<LoopThread *> _threads;   // 线程池
    std::vector<EventLoop *> _evnetLoops; // 线程池对应的loop

public:
    LoopThreadPool(EventLoop *baseloop) : _baseThreadLoop(baseloop), _threadCount(0), _threadIndex(0)
    {
    }

    // 设定线程池内线程数（只能在create（）前有效）
    void SetThreadCount(int threadNum) { _threadCount = threadNum; }

    // 创建线程池（初始化线程池和对应的loop）
    void Create()
    {
        if (_threadCount <= 0)
            return;

        _threads.resize(_threadCount);
        _evnetLoops.resize(_threadCount);
        for (int i = 0; i < _threadCount; i++)
        {
            _threads[i] = new LoopThread();
            _evnetLoops[i] = _threads[i]->GetLoop();
        }
        return;
    }

    //获取下一个线程池中的线程
    EventLoop *NextLoop()
    {
        if (_threadCount == 0)
        {
            return _baseThreadLoop;
        }
        _threadIndex = (_threadIndex + 1) % _threadCount;
        return _evnetLoops[_threadIndex];
    }
};